import math

import numpy


class Vector:
    def __init__(self, *args):
        if len(args) == 1:
            self.x = args[0].x
            self.y = args[0].y
        elif len(args) == 2:
            if args[0] is None:
                raise ValueError("参数不正确")
            if isinstance(args[0], (int, float, numpy.int32)):
                self.x = args[0]
                self.y = args[1]
            else:
                self.x = args[1].x - args[0].x
                self.y = args[1].y - args[0].y
        elif len(args) == 0:
            self.x = 0
            self.y = 0
        else:
            raise ValueError("参数不正确，必须是1个或2个")

        self.angle = math.atan2(self.y, self.x) * 180 / math.pi
        self.angle360 = self.angle % 360 if self.angle % 360 >= 0 else self.angle % 360 + 360
        self.length = math.sqrt(self.x ** 2 + self.y ** 2)

    def get_angle360(self):
        return self.angle % 360 if self.angle % 360 >= 0 else self.angle % 360 + 360

    # //第一次初始创建
    # //角度的获取//返回度数
    @staticmethod
    def get_angle(v1, v2):
        x = v2.x - v1.x
        y = v2.y - v1.y
        angle = math.atan2(y, x) * 180 / math.pi
        return angle

    # 角度的获取//返回度数
    def get_angle(self, v1):
        x = self.x - v1.x
        y = self.y - v1.y
        angle = math.atan2(y, x) * 180 / math.pi
        return angle

    # //向量设置角度
    def set_angle(self, angle):
        self.x = self.length * math.cos(angle * math.pi / 180)
        self.y = self.length * math.sin(angle * math.pi / 180)
        self.angle = angle

    # 大小的获取
    def get_length(self, v2):
        x = v2.x - self.x
        y = v2.y - self.y
        length = math.sqrt(x ** 2 + y ** 2)
        return length

    # 大小的设置
    def set_length(self, l):
        ratio = l / self.length
        self.length = l
        self.scale(ratio)

    def scale(self, s):
        self.x *= s
        self.y *= s

    def __str__(self):
        tx = round(self.x, 2)
        ty = round(self.y, 2)
        angle = round(self.angle, 2)
        length = round(self.length, 2)
        return f"[x={tx}, y={ty}, angle={angle}, length={length}]"

    def reset(self, x, y):
        self.x = x
        self.y = y

    def clone(self):
        return Vector(self.x, self.y)

    # 两个向量相加
    def plus(self, v):
        self.x += v.x
        self.y += v.y

    def plus_new(self, v):
        return Vector(self.x + v.x, self.y + v.y)

    def minus(self, v):
        self.x -= v.x
        self.y -= v.y

    def minus_new(self, v):
        return Vector(self.x - v.x, self.y - v.y)

    def equals(self, v):
        return self.x == v.x and self.y == v.y

    def normal(self):
        length = self.length
        if length == 0: return 0
        return Vector(self.x / length, self.y / length)

    def is_normalized(self):
        return self.length == 1.0

    def dot(self, v):
        return self.x * v.x + self.y * v.y

    # 叉乘(即向量的矢量积)
    def cross_prod(self, v):
        return self.x * v.y - self.y * v.x

    # 当前向量在向量v上的水平投影
    def par_vector(self, v):
        length = v.length
        return v.scale_new((self.x * v.x + self.y * v.y) / (length ** 2))

    # 当前向量在向量v上的垂直投影
    def per_vector(self, v):
        return self.minus_new(self.par_vector(v))

    def distance(self, v):
        tx = v.x - self.x
        ty = v.y - self.y
        return math.sqrt(tx ** 2 + ty ** 2)

    @staticmethod
    def distance(v1, v2):
        tx = v2.x - v1.x
        ty = v2.y - v1.y
        return math.sqrt(tx ** 2 + ty ** 2)

    # 向量求逆，即旋转180度
    def negate(self):
        return Vector(-self.x, -self.y)

    def scale_new(self, s):
        return Vector(self.x * s, self.y * s)

    # 法向量，返回与当前向量垂直的向量
    def get_normal(self):
        return Vector(-self.y, self.x)

    def is_perp_to(self, v):
        return self.dot(v) == 0

    # 向量的夹角//返回弧度
    def angle_between(self, v):
        dp = self.dot(v)
        cos_angle = dp / (self.length * v.length)
        radian = math.acos(cos_angle)
        return radian * 180 / math.pi

    # //向量的夹角//返回弧度
    def radian_between(self, v):
        dp = self.dot(v)
        cos_angle = dp / (self.length * v.length)
        return math.acos(cos_angle)

    # 返回二个向量之间的夹角//返回弧度
    @staticmethod
    def radian_between(v1, v2):
        if not v1.is_normalized():
            v1 = v1.clone().normal()
        if not v2.is_normalized():
            v2 = v2.clone().normal()
        return math.acos(v1.dot(v2))

    # 返回二个向量之间的夹角//返回度数
    @staticmethod
    def angle_between(v1, v2):
        if not v1.is_normalized():
            v1 = v1.clone().normal()
        if not v2.is_normalized():
            v2 = v2.clone().normal()
        return math.acos(v1.dot(v2)) * 180 / math.pi

    # //判定给定的向量是否在本向量的左侧或右侧，左侧返回-1，右侧返回1
    def sign(self, v2):
        return -1 if self.perp().dot(v2) < 0 else 1

    # 返回与本向量垂直的向量(即自身顺时针旋转90度，得到一个新向量)
    def perp(self):
        return Vector(-self.y, self.x)

    # def to_global(self, target):
    #     p = target.local_to_local(self.x, self.y, window.world)
    #     return Vector(p.x, p.y)

    def draw(self, g, v=None):
        if v is None:
            v = Vector()
        length = 10
        angle_offset = 30
        angle = math.atan2(v.y - self.y, v.x - self.x) * 180 / math.pi
        g.clear()
        g.set_stroke_style(1.5)
        g.begin_stroke("#ff0000")
        g.move_to(self.x, self.y)
        g.line_to(self.x + length * math.cos(math.radians(angle - angle_offset)),
                  self.y + length * math.sin(math.radians(angle - angle_offset)))
        g.move_to(self.x, self.y)
        g.line_to(self.x + length * math.cos(math.radians(angle + angle_offset)),
                  self.y + length * math.sin(math.radians(angle + angle_offset)))
        g.move_to(v.x, v.y)
        g.line_to(self.x, self.y)
        g.end_stroke()

    @staticmethod
    def draw(g, v1, v2):
        length = 10
        angle_offset = 30
        angle = math.atan2(v1.y - v2.y, v1.x - v2.x) * 180 / math.pi
        g.clear()
        g.set_stroke_style(1.5)
        g.begin_stroke("#ff0000")
        g.move_to(v2.x, v2.y)
        g.line_to(v2.x + length * math.cos(math.radians(angle - angle_offset)),
                  v2.y + length * math.sin(math.radians(angle - angle_offset)))
        g.move_to(v2.x, v2.y)
        g.line_to(v2.x + length * math.cos(math.radians(angle + angle_offset)),
                  v2.y + length * math.sin(math.radians(angle + angle_offset)))
        g.move_to(v1.x, v1.y)
        g.line_to(v2.x, v2.y)
        g.end_stroke()

    # 左右判断
    def determine_position(a, b):
        difference = a - b
        cross_product = b.cross_prod(difference)

        if cross_product > 0:
            return "a 在 b 的右边"
        elif cross_product < 0:
            return "a 在 b 的左边"
        else:
            return "a 和 b 在同一条直线上"

    # 上下判断
    def determine_vertical_position(a, b):
        if a.y > b.y:
            return "a 在 b 的上面"
        elif a.y < b.y:
            return "a 在 b 的下面"
        else:
            return "a 和 b 在同一高度"

    # 判断两个向量在另一个向量的同一侧：
    def same_side_of(self, v1, v2):
        # 计算向量v1到v2和self到v2的叉积
        cross_product1 = (v1.x - self.x) * (v2.y - self.y) - (v1.y - self.y) * (v2.x - self.x)
        # 计算向量v1到self和v2到self的叉积
        cross_product2 = (v1.x - self.x) * (v2.y - self.y) - (v1.y - self.y) * (v2.x - self.x)

        if cross_product1 * cross_product2 >= 0:
            return True
        else:
            return False

    # 判断两个向量在另一个向量的垂直投影方向：
    def perpendicular_projection(self, v):
        # 计算self在v上的垂直投影向量
        projection = self.minus_new(self.par_vector(v))
        return projection

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __eq__(self, other):
        if isinstance(other, Vector):
            return self.x == other.x and self.y == other.y
        return False

    def __hash__(self):
        return hash((self.x, self.y))

    def __repr__(self):
        return f"Vector(x={self.x}, y={self.y})"


Vector.up = Vector(0, -1)
Vector.right = Vector(-1, 0)
Vector.left = Vector(1, 0)
Vector.down = Vector(0, 1)
